//游戏控制类
(function(window) {
    const X = 15; //地图横向长度，即方块的最大x值
    const Y = 19; //地图纵向长度，即方块的最大y值
    const MAXBLOCK = (1 << (X + 1)) - 1;
    const BKNUM = 7; //俄罗斯方块有7中类型
    const btype = [0, 1, 1, 1, 3, 3, 3]; //七种俄罗斯方块的最大序列号
    const blockColor = ['-webkit-linear-gradient(45deg,#0094ff,#2c2cf0)', '-webkit-linear-gradient(45deg,#ffff2c,#f0f020)', '-webkit-linear-gradient(45deg,#00ffc3,#2cfffe)', '-webkit-linear-gradient(45deg,#ff9400,#ff2c50)', '-webkit-linear-gradient(45deg,#333,#616161)', '-webkit-linear-gradient(45deg,#805d5d,#aa5d4d)', '-webkit-linear-gradient(45deg,#ccc,#d2d2d2)'];
    const SCORE = [0, 10, 30, 60, 100];

    function getRand(l, r) { //获取[l,r]区间内的随机数
        return Math.floor(Math.random() * (r - l + 1) + l);
    }
    class Game {
        constructor() {
            this.paused = 0; //暂停标识
            this.fastLock = 0; //加速锁
            this.score = 0; //游戏的分数
            this.speed = 1; //下落速度
            this.rank = 0; //游戏阶段
            this.block = []; //六种俄罗斯方块的模板对象
            this.block[0] = new Block([
                [0, 1, 0, 1]
            ], [
                [-1, -1, 0, 0]
            ], 1); //正方形方块
            this.block[1] = new Block([
                [0, 1, 2, 3],
                [0, 0, 0, 0]
            ], [
                [0, 0, 0, 0],
                [-3, -2, -1, 0]
            ], 2); //长方形方块
            this.block[2] = new Block([
                [0, 1, 1, 2],
                [1, 0, 1, 0]
            ], [
                [-1, -1, 0, 0],
                [-2, -1, -1, 0]
            ], 2); //Z形方块1
            this.block[3] = new Block([
                [2, 1, 1, 0],
                [0, 1, 0, 1]
            ], [
                [-1, -1, 0, 0],
                [-2, -1, -1, 0]
            ], 2); //Z形方块2
            this.block[4] = new Block([
                [0, 1, 0, 0],
                [0, 1, 2, 2],
                [0, 1, 1, 1],
                [0, 0, 1, 2]
            ], [
                [-2, -2, -1, 0],
                [-1, -1, -1, 0],
                [0, -2, -1, 0],
                [-1, 0, 0, 0]
            ], 4); //L形方块1
            this.block[5] = new Block([
                [0, 1, 1, 1],
                [0, 1, 2, 2],
                [0, 0, 0, 1],
                [0, 1, 2, 0]
            ], [
                [-2, -2, -1, 0],
                [0, 0, 0, -1],
                [-2, -1, 0, 0],
                [-1, -1, -1, 0]
            ], 4); //L形方块2
            this.block[6] = new Block([
                [0, 1, 1, 2],
                [0, 0, 1, 0],
                [0, 1, 1, 2],
                [0, 1, 1, 1]
            ], [
                [0, -1, 0, 0],
                [-2, -1, -1, 0],
                [-1, -1, 0, -1],
                [-1, -2, -1, 0]
            ], 4); //坦克形方块
            this.map = new Map(X + 1, Y + 1, 0, 0, '#fff');
            this.scoreTab = new ScoreTab(7, 7, 3, 0, '#fff');
            this.next = null;
        }
        initial = function() {
            //产生两个个俄罗斯方块，一个是当前的，一个是接下来要出现的
            if (this.next) {
                this.next.remove();
                this.current = this.next;
            } else {
                this.current = this.createBlock();
            }
            this.next = this.createBlock();
            //记录当前方块移动的坐标，方便变换方块时使用
            this.moveX = this.mapXMid;
            this.moveY = 0;
            this.next.render(this.scoreTab.rendered, 2, 5.5);
            //记录渲染后的dom对象
            this.currentDom = this.current.render(this.map.rendered, this.moveX, this.moveY);
        }
        createBlock = function() {
            //从0-6号方块中随机出一个方块，然后再根据方块类型随机出一个状态
            //方块编号
            let num = getRand(0, BKNUM - 1);
            // let num = getRand(6, 6);
            //方块状态
            let status = getRand(0, btype[num]);
            return new Block(this.block[num].x, this.block[num].y, this.block[num].type, status, blockColor[num]);
        }
        start = function() {
            //记录地图的y轴每一行的状态   2进制数表示
            this.blockFlag = [];
            for (let i = -3; i < (Y + 1); i++) {
                this.blockFlag[i] = 0;
            }
            //记录地图的y轴每一行的dom对象
            this.blockMapDom = [];
            for (let i = -3; i < (Y + 1); i++) {
                this.blockMapDom[i] = new Array();
            }
            //移除对地图的渲染
            this.map.remove();
            //重新渲染游戏地图            
            this.map.render();
            //移除对计分板的渲染
            this.scoreTab.remove();
            //重新渲染计分板
            this.scoreTab.render();
            this.scoreTab.mody(this.score, this.rank);
            //计算map中间位置的偏移量
            this.mapXMid = Math.floor((this.map.rendered.clientWidth * 0.5) / rem) - 1;
            //初始化游戏
            this.initial();
            //让当前方块开始移动
            this.autoMove();
            //绑定按键
            this.bindKey();
        }

        autoMove = function() {
            if (this.timer) clearInterval(this.timer);
            this.timer = setInterval(function() {
                if (!this.paused) {
                    this.moveY++; //将方块向下移动
                    if (!legalMap(this.current, this.current.state, this) || !legalBlock(this.current, this.current.state, this.blockFlag, this)) { //判断当前位置的下方位置是否合法，不合法则停止移动方块
                        this.moveY--;
                        if (this.moveY === 0) {
                            this.gameover();
                            clearInterval(this.timer);
                        } else {
                            //将当前方块放入判断数组和dom数组中
                            mapPush(this.current, this.blockFlag, this.blockMapDom, this);
                            eliminate(this.blockFlag, this.blockMapDom, this);
                            let temp = Math.floor(this.score / (this.rank * 200 + 200));
                            this.rank = temp > this.rank ? temp : this.rank;
                            if (this.rank === 10) {
                                return this.win();
                            }
                            this.speed = 1 + this.rank * 0.5;
                            if (this.fastLock) this.speed *= 12;
                            this.scoreTab.mody(this.score, this.rank);
                            this.initial(); //初始化下一个方块
                            this.autoMove();
                        }
                    } else {
                        this.current.remove();
                        this.currentDom = this.current.render(this.map.rendered, this.moveX, this.moveY);
                    }
                }
            }.bind(this), 450 / this.speed);
        }
        bindKey = function() {
            document.addEventListener('keydown', function(e) {
                if (this.paused) return;
                this.current.remove();
                switch (e.keyCode) {
                    case 37:
                        this.moveX--;
                        if (!legalMap(this.current, this.current.state, this) || !legalBlock(this.current, this.current.state, this.blockFlag, this)) {
                            this.moveX++;
                        }
                        break;
                    case 39:
                        this.moveX++;
                        if (!legalMap(this.current, this.current.state, this) || !legalBlock(this.current, this.current.state, this.blockFlag, this)) {
                            this.moveX--;
                        }
                        break;
                    case 40:
                        if (!this.fastLock) {
                            this.fastLock = 1;
                            this.speed *= 12;
                            this.autoMove();
                        }
                        break;
                    case 32:
                    case 38:
                        let temp = (this.current.state + 1) % this.current.type;
                        if (legalMap(this.current, temp, this) && legalBlock(this.current, temp, this.blockFlag, this)) { //变换后的方块是否位置合法
                            this.current.change(temp);
                        }
                        break;
                }
                this.currentDom = this.current.render(this.map.rendered, this.moveX, this.moveY);
            }.bind(this));
            document.addEventListener('keyup', function(e) {
                if (e.keyCode === 40 && !this.paused) {
                    this.fastLock = 0;
                    this.speed /= 12;
                    this.autoMove();
                }
                if (e.keyCode === 27) {
                    this.paused = !this.paused;
                }
            }.bind(this));
        }
        gameover = function() {
            alert('Game over...');
        }
        win = function() {
            alert('太强了，您通关了！');
        }
    }

    function mapPush(obj, flag, dom, game) {
        for (let i = 0; i < 4; i++) {
            let temp = obj.blockBody[obj.state].block[i];
            dom[temp.y + game.moveY].push(game.currentDom[i]);
            flag[temp.y + game.moveY] = flag[temp.y + game.moveY] | (1 << (temp.x + game.moveX));
        }
    }

    function legalBlock(obj, state, flag, game) { //判断对象是否压住下方的方块
        for (let i = 0; i < 4; i++) {
            if ((1 << (obj.blockBody[state].block[i].x + game.moveX)) & flag[obj.blockBody[state].block[i].y + game.moveY]) return 0;
        }
        return 1;
    }

    function legalMap(obj, state, game) { //判断对象是否在地图内
        for (let i = 0; i < 4; i++) {
            if (obj.blockBody[state].block[i].x + game.moveX < 0 || obj.blockBody[state].block[i].x + game.moveX > X || obj.blockBody[state].block[i].y + game.moveY > Y) {
                return 0;
            }
        }
        return 1;
    }

    function eliminate(flag, dom, game) { //消除俄罗斯方块
        let i = Y;
        let f = 0; //记录连续消除数
        while (i > 0) {
            if (!flag[i]) {
                if (f) game.score += SCORE[f];
                break;
            }
            if ((flag[i] ^ MAXBLOCK) === 0) { //消除该行
                f++;
                for (let j = 0; j < dom[i].length; j++) {
                    game.map.rendered.removeChild(dom[i][j]);
                }
                dom[i].length = 0;
                for (let j = i; j > 0; j--) {
                    flag[j] = flag[j - 1]; //后面的行都降一行
                    for (k = 0; k < dom[j - 1].length; k++) {
                        dom[j - 1][k].style.top = `${dom[j-1][k].offsetTop / rem + 1}rem`; //dom对象都向下移一行
                        dom[j].push(dom[j - 1][k]);
                    }
                    dom[j - 1].length = 0;
                }
            } else {
                i--;
                game.score += SCORE[f];
                f = 0;
            }
        }
    }
    window.Game = Game;
}(window))